package com.it.flowable.service.leave.main;

import com.alibaba.fastjson.JSON;
import com.it.flowable.common.constants.BpmCommonConstant;
import com.it.flowable.common.constants.BpmLeaveConstant;
import com.it.flowable.common.vo.common.CommonResult;
import com.it.flowable.entity.leave.*;
import com.it.flowable.mapper.leave.ActFlwServiceDataLeaveVOMapper;
import com.it.flowable.mapper.leave.ActFlwServiceDataRuLeaveVOMapper;
import com.it.flowable.mapper.leave.ActFlwTaskLeaveVOMapper;
import com.it.flowable.service.common.CommonUtils;
import com.it.flowable.service.leave.bpm.BpmLeaveBm;
import com.it.flowable.service.leave.bpm.BpmLeaveDao;
import com.it.flowable.service.leave.bpm.BpmLeavePackParamUtil;
import com.it.flowable.service.leave.bpm.BpmLeaveTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class LeaveMainService {

    @Resource
    private BpmLeaveBm bpmLeaveBm;
    @Resource
    private BpmLeaveDao bpmLeaveDao;
    @Resource
    private BpmLeavePackParamUtil bpmLeavePackParamUtil;
    @Resource
    private BpmLeaveTaskService bpmLeaveTaskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private ProcessEngine processEngine;
    @Resource
    private CommonUtils commonUtils;
    @Resource
    private ActFlwServiceDataLeaveVOMapper actFlwServiceDataLeaveVOMapper;
    @Resource
    private ActFlwTaskLeaveVOMapper actFlwTaskLeaveVOMapper;
    @Resource
    private ActFlwServiceDataRuLeaveVOMapper actFlwServiceDataRuLeaveVOMapper;

    /**
     * 请假申请
     */
    @Transactional(rollbackFor = Exception.class)
    public CommonResult submitApply(Map<String, Object> reqMap) {
        CommonResult commonResult = CommonResult.success();
        log.info("submitApply initParam >>>" + JSON.toJSONString(reqMap));

        try {
            // 其他参数校验再自行补充，可以用spring的validation处理实体类参数校验
            // 处理业务数据
            Integer cDays = MapUtils.getInteger(reqMap, "cDays");// 请假天数至少要大于0
            if (cDays == null || BpmCommonConstant.NUMBER_0.compareTo(cDays) >= 0) {
                commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
                commonResult.setMsg("请假天数必须要大于0!");
                return commonResult;
            }

            String isReApply = MapUtils.getString(reqMap, "isReApply");// 是否重新提交申请
            if (BpmCommonConstant.IS_YES.equals(isReApply)) {// 重新申请
                reApply(commonResult, reqMap);
            } else {// 首次申请
                firstApply(commonResult, reqMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
            commonResult.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            commonResult.setMsg(e.getMessage());
        }

        return commonResult;
    }

    /**
     * 首次提交 请假申请
     *
     * @param commonResult
     * @param reqMap
     */
    public void firstApply(CommonResult commonResult, Map<String, Object> reqMap) throws Exception {
        try {
            // 主表新增数据
            ActFlwMainLeaveVO mainLeaveVO = bpmLeaveDao.insertActFlwMainLeaveVO();
            // 生成申请任务
            String cAppCde = MapUtils.getString(reqMap, "cAppCde");
            ActFlwUserLeaveVO actFlwUserVO = bpmLeaveDao.getActFlwUserVO(cAppCde, null);
            if (actFlwUserVO == null) {
                commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
                commonResult.setMsg("当前用户:" + cAppCde + "不存在!");
                return;
            }
            reqMap.put("cOwnerId", cAppCde);
            reqMap.put("cUserId", cAppCde);
            reqMap.put("cTaskType", BpmLeaveConstant.LEAVE_APPLY);
            reqMap.put("cMainId", mainLeaveVO.getCMainId());
            // 任务信息
            ActFlwTaskLeaveVO taskLeaveVO = bpmLeaveDao.insertActFlwTaskLeaveVO(reqMap);
            // 开始工作流
            // 封装初次请假申请工作流参数
            Map<String, Object> leaveBpmParamMap1 = bpmLeavePackParamUtil.packageStartApplyProcess(mainLeaveVO, taskLeaveVO);
            // todo: 开启请假申请流程，且更新为已接收状态
            String applyTaskId = bpmLeaveBm.startApplyProcess(leaveBpmParamMap1);
            reqMap.put("cTaskId", applyTaskId);

            // 封装一级审批参数
            reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_FIRST_APPROVE);
            // 自动分配一级审批人
            ActFlwUserLeaveVO firstChkUserLeaveVO = bpmLeaveDao.pgCheckCde(null, BpmLeaveConstant.LEAVE_USER_LEVEL_1);
            // 任务信息
            // 将请假申请任务流最新标识更新为0, 且新增1条
            ActFlwTaskLeaveVO firstChkTaskLeaveVO = bpmLeaveDao.updateTaskLeave_Add(taskLeaveVO, reqMap);
            // 工作流业务扩展表
            reqMap.put("cOwnerId", firstChkUserLeaveVO.getCUserId());
            reqMap.put("cUserId", bpmLeaveDao.getCheckCdeList(null, BpmLeaveConstant.LEAVE_USER_LEVEL_1));
            Map<String, Object> leaveBpmParamMap2 = bpmLeavePackParamUtil.packageStartChkProcess(reqMap, firstChkTaskLeaveVO);
            // todo: 将请假申请流程更新为已完成状态，并创建下一流程节点
            bpmLeaveTaskService.completeTask(applyTaskId, leaveBpmParamMap2);

            commonResult.setMsg("提交新请假流程成功!");
            commonResult.setData(mainLeaveVO); // 设置主表信息返回，便于跟踪案件
        } catch (Exception e) {
            log.info("firstApply 提交请假流程失败！");
            e.printStackTrace();
            throw new Exception("创建请假流程失败:" + e.getMessage());
        }
    }

    /**
     * 重新提交 请假申请
     *
     * @param commonResult
     * @param reqMap
     */
    public void reApply(CommonResult commonResult, Map<String, Object> reqMap) throws Exception {
        try {
            String cTaskId = MapUtils.getString(reqMap, "cTaskId");// 如不传，泽查工作流业务表获取
            String isCancelApply = MapUtils.getString(reqMap, "isCancelApply");
            reqMap.put("cTaskType", BpmLeaveConstant.LEAVE_FIRST_APPROVE);

            if (StringUtils.isEmpty(cTaskId)) {
                commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
                commonResult.setMsg("缺失参数[cTaskId]！");
                return;
            }
            ActFlwServiceDataRuLeaveVO taskExtRuVO = actFlwServiceDataRuLeaveVOMapper.selectById(cTaskId);
            if (taskExtRuVO == null) {
                commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
                commonResult.setMsg("无效参数[cTaskId]: " + cTaskId);
                return;
            }
            // 查询当前退回的请假任务信息
            ActFlwTaskLeaveVO taskLeaveVO = bpmLeaveDao.getActFlwTaskLeaveVO(taskExtRuVO.getCMainId(), taskExtRuVO.getCTaskType(), "");
            reqMap.put("cTaskType", taskLeaveVO.getCTaskType());
            reqMap.put("cMainId", taskLeaveVO.getCMainId());
            taskLeaveVO.setIsReApply(BpmCommonConstant.IS_YES);// 标识为重新提交申请
            reqMap.put("cTaskKind", BpmCommonConstant.IS_YES);// 工作流业务表标识退回
            if (BpmCommonConstant.IS_YES.equals(isCancelApply)) {// 取消申请
                taskLeaveVO.setIsCancelApply(BpmCommonConstant.IS_YES);// 标识为取消申请
                taskLeaveVO.setNTrackNum(taskLeaveVO.getNTrackNum() + 1);
                taskLeaveVO.setTUpdTm(new Date());
                actFlwTaskLeaveVOMapper.updateById(taskLeaveVO);
                // 结束流程
                reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_LEAVE_END);
                Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageCancelApplyProcess(reqMap);
                bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);

                commonResult.setMsg("取消请假流程成功!");
            } else {// 重新提交申请
                // 将请假申请任务流最新标识更新为0, 且新增1条
                taskLeaveVO.setCDays(MapUtils.getInteger(reqMap, "cDays"));
                taskLeaveVO.setCAppCde(MapUtils.getString(reqMap, "cAppCde"));
                taskLeaveVO.setCAppRmk(MapUtils.getString(reqMap, "cAppRmk"));
                taskLeaveVO.setCFirstChkOpn("");
                taskLeaveVO.setCFirstChkRmk("");
                taskLeaveVO.setCFirstChkCde("");
                taskLeaveVO.setCSecondChkOpn("");
                taskLeaveVO.setCSecondChkRmk("");
                taskLeaveVO.setCSecondChkCde("");
                reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_FIRST_APPROVE);
                ActFlwTaskLeaveVO reFirstApproveTaskLeaveVO = bpmLeaveDao.updateTaskLeave_Add(taskLeaveVO, reqMap);
                // 自动分配一级审批人
                ActFlwUserLeaveVO firstChkUserLeaveVO = bpmLeaveDao.pgCheckCde(null, BpmLeaveConstant.LEAVE_USER_LEVEL_1);
                // 工作流业务扩展表
                reqMap.put("cOwnerId", firstChkUserLeaveVO.getCUserId());
                reqMap.put("cUserId", bpmLeaveDao.getCheckCdeList(null, BpmLeaveConstant.LEAVE_USER_LEVEL_1));
                Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageStartChkProcess(reqMap, reFirstApproveTaskLeaveVO);
                bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);

                commonResult.setMsg("重新提交请假流程成功!");
            }
        } catch (Exception e) {
            log.info("reApply 提交请假流程失败 >>> " + JSON.toJSONString(reqMap));
            e.printStackTrace();
            throw new Exception("重新提交请假流程失败:" + e.getMessage());
        }
    }

    /**
     * 提交一级审核
     *
     * @param reqMap
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public CommonResult submitFirstChk(Map<String, Object> reqMap) {
        CommonResult commonResult = CommonResult.success();
        log.info("submitFirstChk initParam >>>" + JSON.toJSONString(reqMap));

        String cTaskId = MapUtils.getString(reqMap, "cTaskId");
        String cFirstChkOpn = MapUtils.getString(reqMap, "cFirstChkOpn");
        String cFirstChkRmk = MapUtils.getString(reqMap, "cFirstChkRmk");
        String cFirstChkCde = MapUtils.getString(reqMap, "cFirstChkCde");

        // todo: 如还需要校验任务流程状态，审核人一致性或其他参数，请自行扩展
        // 校验cTaskId，且获取工作流数据
        if (StringUtils.isEmpty(cTaskId)) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("缺失参数[cTaskId]!");
            return commonResult;
        }
        ActFlwServiceDataRuLeaveVO taskExtRuVO = actFlwServiceDataRuLeaveVOMapper.selectById(cTaskId);
        if (taskExtRuVO == null) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("[cTaskId]：!" + cTaskId + "非有效任务号！");
            return commonResult;
        }

        try {
            // 查询任务信息
            ActFlwTaskLeaveVO taskLeaveVO = bpmLeaveDao.getActFlwTaskLeaveVO(taskExtRuVO.getCMainId(), taskExtRuVO.getCTaskType(), "");
            Integer cDays = taskLeaveVO.getCDays();
            reqMap.put("cTaskType", taskExtRuVO.getCTaskType());
            reqMap.put("cMainId", taskLeaveVO.getCMainId());
            reqMap.put("cOperCde", cFirstChkCde);// 模拟当前登录用户编码
            reqMap.put("cTaskKind", BpmCommonConstant.IS_YES.equals(taskLeaveVO.getIsReApply())
                    ?BpmCommonConstant.IS_YES:BpmCommonConstant.IS_NO);
            if (BpmCommonConstant.IS_YES.equals(cFirstChkOpn)) {// 通过
                if (BpmCommonConstant.NUMBER_3.compareTo(cDays) >= 0) {// 不超过3天,流程结束
                    taskLeaveVO.setCFirstChkOpn(cFirstChkOpn);
                    taskLeaveVO.setCFirstChkRmk(cFirstChkRmk);
                    taskLeaveVO.setCFirstChkCde(cFirstChkCde);
                    taskLeaveVO.setNTrackNum(taskLeaveVO.getNTrackNum() + 1);
                    taskLeaveVO.setTUpdTm(new Date());
                    actFlwTaskLeaveVOMapper.updateById(taskLeaveVO);
                    // 完成流程任务
                    reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_LEAVE_END);
                    Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageFirstChkProcessOK3(reqMap, taskLeaveVO);
                    bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);
                    commonResult.setMsg("一级审核通过!");

                } else {// 超过3天，进入二级审批
                    taskLeaveVO.setCFirstChkOpn(cFirstChkOpn);
                    taskLeaveVO.setCFirstChkRmk(cFirstChkRmk);
                    taskLeaveVO.setCFirstChkCde(cFirstChkCde);
                    reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_SECOND_APPROVE);
                    ActFlwTaskLeaveVO secondTaskLeaveVO = bpmLeaveDao.updateTaskLeave_Add(taskLeaveVO, reqMap);
                    // 完成流程任务
                    reqMap.put("cUserId", bpmLeaveDao.getCheckCdeList(null, BpmLeaveConstant.LEAVE_USER_LEVEL_0));
                    Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageFirstChkProcessOK3To5(reqMap, secondTaskLeaveVO);
                    bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);
                    commonResult.setMsg("一级审核通过,进入二级审核!");
                }
            } else {// 退回到请假申请
                taskLeaveVO.setCFirstChkOpn(BpmCommonConstant.IS_NO);
                taskLeaveVO.setCFirstChkRmk(cFirstChkRmk);
                taskLeaveVO.setCFirstChkCde(cFirstChkCde);
                taskLeaveVO.setIsReApply(BpmCommonConstant.IS_YES);//标识为重新提交
                reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_APPLY);
                ActFlwTaskLeaveVO firstChkBackTaskVO = bpmLeaveDao.updateTaskLeave_Add(taskLeaveVO, reqMap);
                // 完成流程任务
                reqMap.put("cOwnerId", firstChkBackTaskVO.getCAppCde());
                reqMap.put("cUserId", firstChkBackTaskVO.getCAppCde());
                Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageFirstChkProcessBack3(reqMap, firstChkBackTaskVO);
                bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);
                commonResult.setMsg("一级审核退回成功");
            }
        } catch (Exception e) {
            log.error("submitFirstChk 提交一级审核失败!");
            e.printStackTrace();
            commonResult.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            commonResult.setMsg("提交一级审核失败: " + e.getMessage());
        }

        return commonResult;
    }

    /**
     * 提交二级审核
     *
     * @param reqMap
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public CommonResult submitSecondChk(Map<String, Object> reqMap) {
        CommonResult commonResult = CommonResult.success();
        log.info("submitSecondChk initParam >>>" + JSON.toJSONString(reqMap));

        String cTaskId = MapUtils.getString(reqMap, "cTaskId");
        String cSecondChkOpn = MapUtils.getString(reqMap, "cSecondChkOpn");
        String cSecondChkRmk = MapUtils.getString(reqMap, "cSecondChkRmk");
        String cSecondChkCde = MapUtils.getString(reqMap, "cSecondChkCde");

        // todo: 如还需要校验任务流程状态，审核人一致性或其他参数，请自行扩展
        // 校验cTaskId，且获取工作流数据
        if (StringUtils.isEmpty(cTaskId)) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("缺失参数[cTaskId]!");
            return commonResult;
        }
        ActFlwServiceDataRuLeaveVO taskExtRuVO = actFlwServiceDataRuLeaveVOMapper.selectById(cTaskId);
        if (taskExtRuVO == null) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("[cTaskId]：!" + cTaskId + "非有效任务号！");
            return commonResult;
        }

        try {
            // 查询任务信息
            ActFlwTaskLeaveVO taskLeaveVO = bpmLeaveDao.getActFlwTaskLeaveVO(taskExtRuVO.getCMainId(), taskExtRuVO.getCTaskType(), "");
            reqMap.put("cTaskType", taskExtRuVO.getCTaskType());
            reqMap.put("cMainId", taskLeaveVO.getCMainId());
            reqMap.put("cOperCde", cSecondChkCde);// 模拟当前登录用户编码
            reqMap.put("cTaskKind", BpmCommonConstant.IS_YES.equals(taskLeaveVO.getIsReApply())
                    ?BpmCommonConstant.IS_YES:BpmCommonConstant.IS_NO);
            if (BpmCommonConstant.IS_YES.equals(cSecondChkOpn)) {// 通过，流程结束
                taskLeaveVO.setCSecondChkOpn(cSecondChkOpn);
                taskLeaveVO.setCSecondChkRmk(cSecondChkRmk);
                taskLeaveVO.setCSecondChkCde(cSecondChkCde);
                taskLeaveVO.setNTrackNum(taskLeaveVO.getNTrackNum() + 1);
                taskLeaveVO.setTUpdTm(new Date());
                actFlwTaskLeaveVOMapper.updateById(taskLeaveVO);
                // 完成流程任务
                reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_LEAVE_END);
                Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageFirstChkProcessOK5(reqMap, taskLeaveVO);
                bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);
                commonResult.setMsg("二级审核通过!");
            } else {// 退回到请假申请
                taskLeaveVO.setCSecondChkOpn(BpmCommonConstant.IS_NO);
                taskLeaveVO.setCSecondChkRmk(cSecondChkRmk);
                taskLeaveVO.setCSecondChkCde(cSecondChkCde);
                taskLeaveVO.setIsReApply(BpmCommonConstant.IS_YES);//标识为重新提交
                reqMap.put("cToTaskType", BpmLeaveConstant.LEAVE_APPLY);
                ActFlwTaskLeaveVO firstChkBackTaskVO = bpmLeaveDao.updateTaskLeave_Add(taskLeaveVO, reqMap);
                // 完成流程任务
                reqMap.put("cOwnerId", firstChkBackTaskVO.getCAppCde());
                reqMap.put("cUserId", firstChkBackTaskVO.getCAppCde());
                Map<String, Object> leaveBpmParamMap = bpmLeavePackParamUtil.packageFirstChkProcessBack5(reqMap, firstChkBackTaskVO);
                bpmLeaveTaskService.completeTask(cTaskId, leaveBpmParamMap);
                commonResult.setMsg("二级审核退回成功");
            }
        } catch (Exception e) {
            log.error("submitSecondChk 提交二级审核失败!");
            e.printStackTrace();
            commonResult.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            commonResult.setMsg("提交二级审核失败: " + e.getMessage());
        }

        return commonResult;
    }

    /**
     * 编辑任务状态：解除接收、已接收等
     *
     * @param reqMap
     * @return
     */
    public CommonResult editTask(Map<String, Object> reqMap) {
        CommonResult commonResult = CommonResult.success();
        String taskStatus = MapUtils.getString(reqMap, "taskStatus");
        String cOwnerId = MapUtils.getString(reqMap, "cOwnerId");
        String cTaskId = MapUtils.getString(reqMap, "cTaskId");
        log.info("editTask param >>> " + JSON.toJSONString(reqMap));

        if (StringUtils.isEmpty(taskStatus) || StringUtils.isEmpty(cOwnerId) || StringUtils.isEmpty(cTaskId)) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("缺失参数[taskStatus]|[cOwner]|[cTaskId]!");
            return commonResult;
        }
        ActFlwServiceDataLeaveVO taskExtVO = actFlwServiceDataLeaveVOMapper.selectById(cTaskId);
        if (taskExtVO == null) {
            commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
            commonResult.setMsg("非有效参数[cTaskId]");
            return commonResult;
        }

        try {
            if (BpmLeaveConstant.LEAVE_STATUS_ACCEPT.equals(taskStatus)) {// 已接收
                if (BpmLeaveConstant.LEAVE_APPROVE_LIST.contains(taskExtVO.getCTaskType())) {
                    if (!taskExtVO.getCUserId().contains(cOwnerId)) {
                        commonResult.setCode(HttpStatus.SC_BAD_REQUEST);
                        commonResult.setMsg("[cOwnerId]:" + cOwnerId + "非审核人其中之一！");
                        return commonResult;
                    }
                }
                // 如果是审核环节，需要校验下传参处理人，是否在任务池中
                bpmLeaveTaskService.acceptTask(cTaskId, cOwnerId);
            } else if (BpmLeaveConstant.LEAVE_STATUS_INIT.equals(taskStatus)){// 解除接收
                // 回归为初始态，且将cOwnerId置空，待处理
            }
        } catch (Exception e) {
            log.error("editTask fail， param >>> " + JSON.toJSONString(reqMap));
            e.printStackTrace();
            commonResult.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            commonResult.setMsg(e.getMessage());
        }

        return commonResult;
    }

    /**
     * 生成流程图图片
     *
     * @param response
     * @param processId
     */
    public void showPic(HttpServletResponse response, String processId) {
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (null == instance) {
            return;
        }
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processId).list();
        List<String> activeIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution execution : executions) {
            List<String> activeActivityIds = runtimeService.getActiveActivityIds(execution.getId());
            activeIds.addAll(activeActivityIds);
        }

        /**
         * 生成流程图
         */
        BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
        ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engConf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activeIds, flows, engConf.getActivityFontName(),
                engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(), 1.0, false);

        // 设置响应头，通知浏览器端下载文件
        response.setHeader("Content-Disposition", "attachment; filename=\"example.png\"");
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int length = 0;
        try {
            out = response.getOutputStream();
            while ((length = in.read(buf)) != -1) {
                out.write(buf, 0, length);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
